package org.mbds.wolf.android.hce.seservice;

import java.io.File;
import java.nio.charset.Charset;
//import java.security.KeyPair;
//import java.security.KeyPairGenerator;
//import java.security.PrivateKey;
//import java.security.PublicKey;
//import java.text.SimpleDateFormat;
//import java.util.Date;
import java.util.List;

import org.mbds.wolf.exception.ByteArrayToHexaStringException;
import org.mbds.wolf.exception.HexaStringToByteArrayException;
//import org.mbds.wolf.exception.WolfException;
import org.mbds.wolf.seql.APDUCommand;
import org.mbds.wolf.seql.APDUModel;
import org.mbds.wolf.seql.APDUResponse;
import org.mbds.wolf.seql.AppletModel;
import org.mbds.wolf.seql.Data;
import org.mbds.wolf.seql.SeqlWords;
import org.mbds.wolf.seql.exceptions.APDUClaException;
import org.mbds.wolf.seql.exceptions.APDUDataLengthException;
import org.mbds.wolf.seql.exceptions.APDUInsException;
import org.mbds.wolf.seql.exceptions.APDUp1p2Exception;
import org.mbds.wolf.tools.BytesTool;

import android.content.ContentValues;
import android.content.Context;
import android.content.res.Resources.NotFoundException;
import android.database.Cursor;
import android.database.sqlite.SQLiteCantOpenDatabaseException;
import android.database.sqlite.SQLiteDatabase;
import android.os.Bundle;
import android.util.Log;

/**
 * 
 * @author alesas
 *
 */
public class WolfApplet 
{

	public final static String TAG = "WolfApplet";

	public static enum Status 
	{
        INSTALLED, SELECTABLE, PERSONALIZED, LOCKED;
	}; 

	public final static String FIELD_PIN 		= "PIN";
	
	public final static String TABLE_FIELDS 	= "fields_table";
	public final static String COLUMN_ID 		= "_id";
	public final static String COLUMN_FIELD 	= "field";
	public final static String COLUMN_DATA 		= "data";
	public final static String COLUMN_LENGTH 	= "length";
	public final static String VAR_FIELD 		= "%FIELD%";
	public final static String VAR_VALUE 		= "%VALUE%";
	public final static String VAR_TABLE 		= "%TABLE%";
	public final static String VAR_ID 			= "%ID%";
	public final static String VAR_CLAUSE 		= "%CLAUSE%";
	public final static String VAR_COLUMNS 		= "%COLUMNS%";
	public final static String VAR_COLUMNS_DESC	= "%COLUMNS_DESC%";
	public final static String VAR_VALUES 		= "%VALUES%";
	public final static String SQL_CREATE 		= "create table "+VAR_TABLE+" ("+VAR_COLUMNS_DESC+") ";
	public final static String SQL_INSERT 		= "insert into "+VAR_TABLE+" ("+VAR_COLUMNS+") VALUES("+VAR_VALUES+")";
	public final static String SQL_SELECT 		= "select "+VAR_COLUMNS+" from "+VAR_TABLE;
	public final static String SQL_WHERE 		= " where "+VAR_CLAUSE;
	
	private Status status 				= Status.INSTALLED; 
	private String alias 				= "";
	private byte[] AID;
	private String version 				= "";
	private boolean isSelected 			= false;
	private boolean isAuthenticated		= false;
	private boolean isPersonalized		= false;
	private String selectApdu;
	private SQLiteDatabase database;
	private boolean isDatabaseLoaded	= false;
	private String dbName 				= null;
	private AppletModel model 			= null;
	private Context context;
//	private int messageCounter 			= 0;
//	private PrivateKey privateKey;
//	private PublicKey publicKey;
//	private PublicKey clientKey;
//	private String pinCode 				= "0000";
//	private int pinMaxTry 				= 0;
//	private int pinTryCounter 			= 0;
//	private long timeOut;
//	private boolean isLocked 			= false;
//	private SimpleDateFormat dateFormat	= new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
	
	/**
	 * Constructor WolfApplet
	 * @param model
	 * @param ctx
	 */
	public WolfApplet(AppletModel model, Context ctx) 
	{
		super();
		setStatus(Status.INSTALLED); 
		this.context = ctx;
		this.model = model;
		
		try 
		{
			String aid = model.getAID();
			setAID(BytesTool.hexStringToByteArray(aid));
			byte[] lc;
			Integer i = Integer.valueOf(AID.length);
			
			if (i.byteValue()>0xFF) 
			{
				lc = BytesTool.integerToByteArray(i.intValue(), 3);
				lc[0] = 0x00;
			} 
			else 
			{
				lc = new byte[1];
				lc[0] = i.byteValue();
			}
			
			// Build the SELECT AID APDU STANDARD 7816-4 for comparison
			selectApdu = BytesTool.byteArrayToHexString(APDUModel.CMD_SELECT_AID) + "00 "
				+ BytesTool.byteArrayToHexString(lc)+getAIDString();
			Log.d(TAG, model.getAlias()+" Select APDU: " + selectApdu);
		} 
		catch (HexaStringToByteArrayException e) 
		{
			e.printStackTrace();
		} 
		catch (NotFoundException e) 
		{
			e.printStackTrace();
		} 
		catch (ByteArrayToHexaStringException e) 
		{
			e.printStackTrace();
		}
		
		if (loadConfig(model))
		{
			setStatus(Status.SELECTABLE);
		}
	}
	
	/**
	 * loadConfig
	 * @param model
	 * @return boolean
	 */
	private boolean loadConfig(AppletModel model) 
	{
		if (database!=null && isDatabaseLoaded())
		{
			return true;
		}
		
		// Local DB name is the AID of the service
		dbName = getAIDString().replaceAll(" ",  "");
		setAlias(model.getAlias());
		
		try 
		{
			File dbFile= context.getDatabasePath(dbName);
			// open the database and return
			database = SQLiteDatabase.openDatabase(dbFile.getAbsolutePath(), null, android.content.Context.MODE_PRIVATE);
			database.close();
			setDatabaseLoaded(true);
			return true;
		} 
		catch (SQLiteCantOpenDatabaseException e2) 
		{
			e2.printStackTrace();// database cannot be open
		} 
		catch (Exception e3) 
		{
			// database does not exist
		}
		
		if (database == null)
		{
			try 
			{
				// Create database using config metadata (AppletModels)
				try 
				{
					database = context.openOrCreateDatabase(dbName, android.content.Context.MODE_PRIVATE, null);
				}
				catch (Exception e)
				{
					//
				}
				
				String sql = SQL_CREATE.replace(VAR_TABLE, 
						TABLE_FIELDS).replace(VAR_COLUMNS_DESC, COLUMN_ID+" INTEGER PRIMARY KEY,"
								+COLUMN_FIELD+" TEXT,"+COLUMN_DATA+" TEXT,"+COLUMN_LENGTH+" INTEGER");
				
				Log.d(TAG, model.getAlias()+" SQL: " + sql);
				
				database.execSQL(sql);
				
				List<Data> fields = model.getData();
				long rowId = -1;
				if (fields!=null && !fields.isEmpty())
				{
					for (Data f: fields) 
					{
						String name = f.getName();
						String hexaId =  f.getId();
						int len = f.getLength();
						
						sql = SQL_CREATE.replace(VAR_TABLE, 
								name).replace(VAR_COLUMNS_DESC, COLUMN_ID+" INTEGER PRIMARY KEY,"
										+COLUMN_DATA+" TEXT");
						
						Log.d(TAG, model.getAlias()+" SQL: " + sql);
						
						database.execSQL(sql);
						
						ContentValues content = new ContentValues();
						content.put(COLUMN_FIELD, name);
						content.put(COLUMN_DATA, hexaId);
						content.put(COLUMN_LENGTH, len);
						
						Log.d(TAG, model.getAlias()+" SQL insert: " + TABLE_FIELDS+" "+content.toString());
						
						rowId = database.insert(TABLE_FIELDS, null, content);
						
						Log.d(TAG, model.getAlias()+" rowId: " + rowId);
						
					}
					setDatabaseLoaded(rowId!=-1);
				}
				else
				{
					setDatabaseLoaded(database!=null);
				}
			} 
			catch (Exception e) 
			{
				e.printStackTrace();
			}
			finally
			{
				try
				{
					database.close();
				}
				catch (Exception e)
				{
					//
				}
			}
		}
		return isDatabaseLoaded();
	}	
	
	/**
	 * initialize
	 * @param AID
	 * @param alias
	 * @param version
	 * @return boolean
	 */
	public boolean initialize(byte[] aID, String alias, String version) 
	{
		setAlias(alias.toUpperCase().trim());
		setVersion(version.trim());
		return true;
	}

	/**
	 * processCommandApdu
	 * @param byte[] commandApdu
	 * @param Bundle extras
	 * @return byte[] APDU response (bytes array)
	 */
	public byte[] processCommandApdu(byte[] commandApdu, Bundle extras) 
	{
//		messageCounter++;
		if (getStatus()!= Status.SELECTABLE) 
		{
			// the Applet must be initialized!...
			Log.e(TAG, getAlias()+" Applet not initialized!!");
			return APDUModel.SW_COND_OF_USE_NOT_SAT;
		}
		
        if (!isSelected()) 
        {
        	isAuthenticated = false;
        	// Applet must be selected first
			if (selectAidApdu(commandApdu)) 
			{
				// if this is the SELECT AID instruction, welcome message in the success response
				// Applet is selected and ready to receive commands
				return getWelcomeMessage();
			} 
			else if (commandApdu.length >= 2 && commandApdu[1] == APDUModel.INS_SELECT_FILE) 
			{
				// Incorrect AID (might never be the case as the instruction is routed ti the appropriate applet!
				try 
				{
					Log.e(TAG, getAlias()+" Incorrect AID: " + BytesTool.byteArrayToHexString(commandApdu));
				} 
				catch (ByteArrayToHexaStringException e) 
				{
					e.printStackTrace();
				}
				APDUResponse resp = new APDUResponse();
				resp.setData(commandApdu);
				resp.setStatusWord(APDUModel.SW_FILE_NOT_FOUND);
	            return resp.getResponse();
			} 
			else 
			{
				// Instruction is not the expected SELECT AID standard ISO 7816-4:
				// No other instruction will be processed without a previous SELECT AID instruction!
	            try 
	            {
					Log.e(TAG, getAlias()+" CMD is not the SELECT_AID: " + BytesTool.byteArrayToHexString(commandApdu));
				} 
	            catch (ByteArrayToHexaStringException e) 
	            {
					e.printStackTrace();
				}
	            return APDUModel.SW_GENERAL_ERROR;
			}
        } 
        else if (selectAidApdu(commandApdu)) 
        {
        	// Applet was already selected but is is again a SELECT AID instruction:
        	// Response welcome message success...
 			return getWelcomeMessage();
		}
        
        // Applet was selected and a new command is received...
		try 
		{
			APDUCommand apduCmd = new APDUCommand(commandApdu);
			
			// Get the SeQL type of the instruction
			String instruction = model.getSeqlType(apduCmd.getCla(),  apduCmd.getIns());

			if (instruction.equalsIgnoreCase(SeqlWords.INSERT)) 
			{
				// INSERT data
				return insertData(apduCmd).getResponse();
			} 
			else if (instruction.equalsIgnoreCase(SeqlWords.CREATE)) 
			{
				// CREATE data
				return createData(apduCmd).getResponse();
			} 
			else if (instruction.equalsIgnoreCase(SeqlWords.UPDATE)) 
			{
				// UPDATE data
				return updateData(apduCmd).getResponse();
			} 
			else if (instruction.equalsIgnoreCase(SeqlWords.SELECT)) 
			{
				// SELECT data
				return selectData(apduCmd).getResponse();
			} 
			else if (instruction.equalsIgnoreCase(SeqlWords.DELETE)) 
			{
				// DELETE data
				return deleteData(apduCmd).getResponse();
			} 
			else if (instruction.equalsIgnoreCase(SeqlWords.CHECK)) 
			{
				// CHECK: this is a comparison between provided value and field value
				return checkData(apduCmd).getResponse();
			} 
			else if (instruction.equalsIgnoreCase(SeqlWords.AUTH)) 
			{
				// AUTHENTICATE (CHECK a PIN code)
				return authenticate(apduCmd).getResponse();
			} 
			else 
			{
				// return invalid instruction (or not yet  implemented)
				return APDUModel.SW_INVALID_INS;
			}
		} 
		// Parsing the APDU command exception
		catch (Exception e) 
		{
			if (e instanceof APDUClaException) 
			{
				// invalid CLA byte
				return APDUModel.SW_INVALID_CLA;
			} 
			else if (e instanceof APDUInsException) 
			{
				// Invalid INS byte
				return APDUModel.SW_INVALID_INS;
			} 
			else if ((e instanceof APDUp1p2Exception)) 
			{
				// invalid P1 / P2 byte
				return APDUModel.SW_INVALID_P1P2;
			} 
			else if (e instanceof APDUDataLengthException) 
			{
				// invalid LC
				return APDUModel.SW_INVALID_LC;
			} 
			else 
			{
				// invalid format
				return APDUModel.SW_INVALID_FORMAT;
			}
		}
	}
	
	/**
	 * getWelcomeMessage
	 * @return byte[] welcome message success status words
	 */
	private byte[] getWelcomeMessage() 
	{
		setSelected(true);
		
		Log.i(TAG, "Welcome message: WolfHceService "+getAlias()+ " AID: "+getAIDString());
		byte[] data = (TAG+": "+getAlias()).getBytes(Charset.forName("UTF-8"));
		
		APDUResponse resp = new APDUResponse();
		resp.setData(data);
		resp.setStatusWord(APDUModel.SW_SUCCESS);
		
		try 
		{
			Log.i(TAG, "Welcome message: WolfHceService hex: "+BytesTool.byteArrayToHexString(resp.getResponse()));
		} 
		catch (ByteArrayToHexaStringException e) 
		{
			//
		}
		return resp.getResponse();
	}
	
	/**
	 * selectAidApdu
	 * @param apdu
	 * @return boolean is the select aid Ok
	 */
	public boolean selectAidApdu(byte[] apdu) 
	{
		if (apdu.length >= 2 && apdu[1] == APDUModel.INS_SELECT_FILE) 
		{
			try 
			{
				String hexaStr = BytesTool.byteArrayToHexString(apdu).toUpperCase();
				Log.d(TAG, getAlias()+" My selectAid: "+hexaStr+", received selecAID: "+selectApdu);
				
				if (hexaStr.equals(selectApdu) || hexaStr.equals(selectApdu+"00 ")) 
				{
					return true;
				}
			} 
			catch (ByteArrayToHexaStringException e) 
			{
				e.printStackTrace();
			}
		}
		return false;
	}

	/**
	 * getFieldName from table of fields
	 * @param p1 Hexa Id
	 * @return String field name
	 */
	private String getFieldName(byte p1) {
		String field = null;
		try 
		{
			try 
			{
				// Open database
				database = context.openOrCreateDatabase(dbName, android.content.Context.MODE_PRIVATE, null);
			}
			catch (Exception e)
			{
				//
			}			
			// get the field name from the table of fields
			String sql = SQL_SELECT.replace(VAR_TABLE, TABLE_FIELDS).replace(VAR_COLUMNS, COLUMN_FIELD);
			sql += SQL_WHERE.replace(VAR_CLAUSE, COLUMN_DATA+"=?");
			Log.d(TAG, getAlias()+" SQL query getFieldName: " + sql);
			
			Cursor c = database.rawQuery(sql, new String[]{BytesTool.byteToHexaString(p1).trim()});
			
			if (c!=null && c.moveToFirst()) 
			{
				field = c.getString(c.getColumnIndex(COLUMN_FIELD));
			}
			Log.d(TAG, getAlias()+" return: " + field);
		} catch (Exception e) 
		{
			e.printStackTrace();
		}
		finally
		{
			try
			{
				database.close();
			}
			catch (Exception e)
			{
				//
			}
		}
		return field;
	}

	/**
	 * createData
	 * @param cmd
	 * @return APDUResponse
	 */
	private APDUResponse createData(APDUCommand cmd) 
	{
		APDUResponse resp = new APDUResponse();
		
		if (cmd.getApduCommand().length<6) 
		{
			resp.setResponse(APDUModel.SW_INVALID_DATA);
		}
		
		try 
		{
			try 
			{
				// Open database
				database = context.openOrCreateDatabase(dbName, android.content.Context.MODE_PRIVATE, null);
			}
			catch (Exception e)
			{
				//
			}
			String name = new String(cmd.getData(), Charset.forName("UTF-8"));
			String hexaId = BytesTool.byteToHexaString(cmd.getP1()); // Data ID
			long len = BytesTool.byteArrayToIntValue(cmd.getLe());
			
			// Create the entry (table) in the database:
			String sql = SQL_CREATE.replace(VAR_TABLE, name)
					.replace(VAR_COLUMNS_DESC, COLUMN_ID+" INTEGER PRIMARY KEY,"
							+COLUMN_DATA+" TEXT");;
			Log.d(TAG, getAlias()+" SQL: " + sql);
			
			database.execSQL(sql);
						
			ContentValues content = new ContentValues();
			content.put(COLUMN_FIELD, name); // Alias of the field
			content.put(COLUMN_DATA, hexaId);  // Hexa Id
			content.put(COLUMN_LENGTH, len); // length
			
			Log.d(TAG, getAlias()+" SQL insert: " + TABLE_FIELDS+" "+content.toString());	
			
			// Insert the entry in the table of fields (list of available tables):
			database.insert(TABLE_FIELDS, null, content);	
			
			resp.setStatusWord(APDUModel.SW_SUCCESS);
			// If data is provided: insert data
			if (cmd.getData()!=null)
			{
				// P1 contains the field ID and Data contains the data to be inserted
				resp = insertData(cmd);
			}
		} 
		catch (Exception e) 
		{
			resp.setStatusWord(APDUModel.SW_GENERAL_ERROR);
		}
		finally
		{
			try
			{
				database.close();
			}
			catch (Exception e)
			{
				//
			}

		}
		return resp;
	}

	/**
	 * insertData
	 * @param cmd
	 * @return APDUResponse
	 */
	private APDUResponse insertData(APDUCommand cmd) 
	{
		APDUResponse resp = new APDUResponse();
		
		if (cmd.getApduCommand().length<6) 
		{
			// No data provided
			resp.setResponse(APDUModel.SW_INVALID_DATA);
			return resp;
		}
		
		String field = getFieldName(cmd.getP1());
		
		if (field==null)
		{
			// no field ID provided
			resp.setResponse(APDUModel.SW_INCORRECT_P1P2);
			return resp;
		}
		
		if (field.equalsIgnoreCase(FIELD_PIN))
		{
			setPersonalized(true);
		}
		
		try 
		{
			try 
			{
				// Open database
				database = context.openOrCreateDatabase(dbName, android.content.Context.MODE_PRIVATE, null);
			}
			catch (Exception e)
			{
				//
			}

			ContentValues val = new ContentValues();
			val.put(COLUMN_DATA, new String(cmd.getData(), Charset.forName("UTF-8")));
			Log.d(TAG, getAlias()+" SQL insert: " + field+" "+val.toString());
			
			long rowId = database.insert(field, null, val);
			
			Log.d(TAG, getAlias()+"."+field+".rowId: " + rowId);
			
			if (rowId!=-1) 
			{
				// Data was successfully inserted
				resp.setData(BytesTool.integerToByteArray((int) rowId, 4));
				resp.setStatusWord(APDUModel.SW_SUCCESS);
			} 
			else 
			{
				// Data was not inserted: return error
				resp.setStatusWord(APDUModel.SW_GENERAL_ERROR);
			}
		} 
		catch (Exception e) 
		{
			resp.setData((e.getMessage().substring(0, (e.getMessage().length()>20?20:e.getMessage().length()))).getBytes());
			resp.setStatusWord(APDUModel.SW_GENERAL_ERROR);
		}
		finally
		{
			try
			{
				database.close();
			}
			catch (Exception e)
			{
				//
			}

		}
		return resp;
	}
 

	/**
	 * selectData
	 * @param cmd
	 * @return APDUResponse
	 */
	private APDUResponse selectData(APDUCommand cmd) 
	{
		APDUResponse resp = new APDUResponse();
		String field = getFieldName(cmd.getP1());
		
		if (field==null)
		{
			resp.setResponse(APDUModel.SW_INCORRECT_P1P2);
			return resp;
		}
		
		try 
		{
			try 
			{
				// Open database
				database = context.openOrCreateDatabase(dbName, android.content.Context.MODE_PRIVATE, null);
			}
			catch (Exception e)
			{
				//
			}
			String sql = SQL_SELECT.replace(VAR_TABLE, field).replace(VAR_COLUMNS, COLUMN_DATA);

			String[] where = null;
			byte P2 = cmd.getP2();
			
			if (P2!=0x00)
			{
				sql+=" where "+COLUMN_ID+"=?";
				where = new String[]{""+BytesTool.byteArrayToIntValue(new byte[]{P2})};
			}
			
			Log.d(TAG, getAlias()+" SQL query: " + sql+(where==null?"":where));
			Cursor c = database.rawQuery(sql, where);
			String res = null;
			
			if (c!=null && c.moveToFirst()) 
			{
				res = "";
				do 
				{
					res += c.getString(c.getColumnIndex(COLUMN_DATA));
					Log.d(TAG, getAlias()+"."+field+".result: " + res);
					
					if (!c.isLast())
					{
						//CSV separator for list of values
						res += SeqlWords.STR_SEPARATOR;
					}
					else
					{
						break;
					}
				} 
				while(c.moveToNext());
			}
			
			if (res!=null) 
			{
				Log.d(TAG, getAlias()+" Result: " + res);
				resp.setData(res.getBytes());
				resp.setStatusWord(APDUModel.SW_SUCCESS);
			} 
			else 
			{
				resp.setStatusWord(APDUModel.SW_INCORRECT_P1P2);
			}
		} 
		catch (Exception e1) 
		{
			Log.e(TAG, e1.getMessage());
			resp.setData((e1.getMessage().substring(0, (e1.getMessage().length()>20?20:e1.getMessage().length()))).getBytes());
			resp.setStatusWord(APDUModel.SW_GENERAL_ERROR);
		} 
		finally
		{
			try
			{
				database.close();
			}
			catch (Exception e)
			{
				//
			}

		}
		return resp;
	}

	/**
	 * updateData
	 * @param cmd
	 * @return APDUResponse
	 */
	private APDUResponse updateData(APDUCommand cmd) 
	{
		APDUResponse resp = new APDUResponse();
		
		if (cmd.getApduCommand().length<6) 
		{
			resp.setResponse(APDUModel.SW_INVALID_DATA);
			return resp;
		}
		
		String field = getFieldName(cmd.getP1());
		
		if (field==null)
		{
			resp.setResponse(APDUModel.SW_INCORRECT_P1P2);
			return resp;
		}
		
		try 
		{
			try 
			{
				// Open database
				database = context.openOrCreateDatabase(dbName, android.content.Context.MODE_PRIVATE, null);
			}
			catch (Exception e)
			{
				//
			}
			
			ContentValues val = new ContentValues();
			val.put(COLUMN_DATA, new String(cmd.getData(), Charset.forName("UTF-8")));
			Log.d(TAG, getAlias()+" SQL update: " + field+" "+val.toString());
			
			String whereClause = null;
			String[] where = null;
			byte P2 = cmd.getP2();
			if (P2!=0x00)
			{
				whereClause=COLUMN_ID+"=?";
				where = new String[]{""+BytesTool.byteArrayToIntValue(new byte[]{P2})};
			}
			
			long nbRows = database.update(field, val, whereClause, where);
			
			Log.d(TAG, getAlias()+"."+field+".rowId: " + nbRows);
			
			if (nbRows!=0) 
			{
				resp.setData(BytesTool.integerToByteArray((int) nbRows, 4));
				resp.setStatusWord(APDUModel.SW_SUCCESS);
			} 
			else 
			{
				resp.setStatusWord(APDUModel.SW_GENERAL_ERROR);
			}
		} 
		catch (Exception e) 
		{
			resp.setData((e.getMessage().substring(0, (e.getMessage().length()>20?20:e.getMessage().length()))).getBytes());
			resp.setStatusWord(APDUModel.SW_GENERAL_ERROR);
		}
		finally
		{
			try
			{
				database.close();
			}
			catch (Exception e)
			{
				//
			}

		}
		return resp;
	}

	/**
	 * deleteData
	 * @param cmd
	 * @return APDUResponse
	 */
	private APDUResponse deleteData(APDUCommand cmd) 
	{
		APDUResponse resp = new APDUResponse();
		
		String field = getFieldName(cmd.getP1());
		Log.d(TAG, getAlias()+" SQL delete field: "+field);
		
		if (field==null)
		{
			resp.setResponse(APDUModel.SW_INCORRECT_P1P2);
			return resp;
		}
		
		try 
		{
			try 
			{
				// Open database
				database = context.openOrCreateDatabase(dbName, android.content.Context.MODE_PRIVATE, null);
			}
			catch (Exception e)
			{
				//
			}
			
			String whereClause = null;
			String[] where = null;
			byte P2 = cmd.getP2();
			if (P2!=0x00)
			{
				whereClause=COLUMN_ID+"=?";
				where = new String[]{""+BytesTool.byteArrayToIntValue(new byte[]{P2})};
			}
			
			
			Log.d(TAG, getAlias()+" SQL delete: " + field+" "+whereClause+", "+where);
			int nbRows = database.delete(field, whereClause, where); 
			
			if (nbRows!=0) 
			{
				Log.d(TAG, getAlias()+" Result: " + nbRows);
				resp.setData(BytesTool.integerToByteArray(nbRows));
				resp.setStatusWord(APDUModel.SW_SUCCESS);
			} 
			else 
			{
				resp.setStatusWord(APDUModel.SW_INCORRECT_P1P2);
			}
		} 
		catch (Exception e1) 
		{
			Log.e(TAG, e1.getMessage());
			resp.setData((e1.getMessage().substring(0, (e1.getMessage().length()>20?20:e1.getMessage().length()))).getBytes());
			resp.setStatusWord(APDUModel.SW_GENERAL_ERROR);
		} 
		finally
		{
			try
			{
				database.close();
			}
			catch (Exception e)
			{
				//
			}

		}
		return resp;	
	}
	
	/**
	 * checkData
	 * @param cmd
	 * @return APDUResponse
	 */
	private APDUResponse checkData(APDUCommand cmd) 
	{
		// TODO: manage PIN as a field?
		APDUResponse resp = new APDUResponse();
		resp.setStatusWord(APDUModel.SW_VERIFICATION_FAILED);
		APDUResponse data = selectData(cmd);
		if (data.getStatusWord()[0]==APDUModel.SW_SUCCESS[0] && data.getStatusWord()[1]==APDUModel.SW_SUCCESS[1])
		{
			try {
				if (BytesTool.byteArrayToHexString(data.getData()).trim()
						.equalsIgnoreCase(BytesTool.byteArrayToHexString(cmd.getData()).trim()))
				{
					resp.setStatusWord(APDUModel.SW_SUCCESS);
				}
			} catch (ByteArrayToHexaStringException e) {
				//
			}
		}
		return resp;	
	}

	/**
	 * authenticate
	 * @param cmd
	 * @return APDUResponse
	 */
	private APDUResponse authenticate(APDUCommand cmd) 
	{
		APDUResponse resp = new APDUResponse();
    	isAuthenticated = false;
		
		if (!isPersonalized())
		{
			resp.setStatusWord(APDUModel.SW_INS_NOT_SUPPORTED);
			return resp;
		}

		String field = getFieldName(cmd.getP1());
		if (!field.equalsIgnoreCase(FIELD_PIN))
		{
			resp.setStatusWord(APDUModel.SW_INCORRECT_P1P2);
			return resp;
		}
		
		resp.setStatusWord(APDUModel.SW_VERIFICATION_FAILED);
		if (APDUModel.isResponseSucceded(checkData(cmd).getStatusWord())) 
		{
        	isAuthenticated = true;
			resp.setStatusWord(APDUModel.SW_SUCCESS);
		}
		return resp;	
	}
	
	
	/**
	 * getAlias
	 * @return the alias
	 */
	public String getAlias() 
	{
		return alias;
	}

	/**
	 * setAlias
	 * @param alias the alias to set
	 */
	private void setAlias(String alias) 
	{
		this.alias = alias;
	}

	/**
	 * getAID
	 * @return the AID
	 */
	public byte[] getAID() 
	{
		return AID;
	}
	
	/**
	 * getAIDString
	 * @return the AID String
	 */
	public String getAIDString() 
	{
		String str = new String(AID);
		
		try 
		{
			str = BytesTool.byteArrayToHexString(AID);
		} 
		catch (ByteArrayToHexaStringException e) 
		{
			e.printStackTrace();
		}
		return str;
	}

	/**
	 * setAID
	 * @param AID the AID to set
	 */
	private void setAID(byte[] aID) 
	{
		this.AID = aID;
	}

	/**
	 * getVersion
	 * @return the version
	 */
	public String getVersion() 
	{
		return version;
	}

	/**
	 * setVersion
	 * @param version the version to set
	 */
	private void setVersion(String version) 
	{
		this.version = version;
	}

	/**
	 * getStatus
	 * @return the status
	 */
	public Status getStatus() 
	{
		return status;
	}

	/**
	 * setStatus
	 * @param status the status to set
	 */
	private void setStatus(Status status) 
	{
		this.status = status;
	}

	/**
	 * isSelected
	 * @return the isSelected
	 */
	public boolean isSelected() 
	{
		return isSelected;
	}

	/**
	 * setSelected
	 * @param isSelected the isSelected to set
	 */
	private void setSelected(boolean isSelected) 
	{
		this.isSelected = isSelected;
	}

	/**
	 * isPersonalized
	 * @return the isPersonalized
	 */
	public boolean isPersonalized() 
	{
		return isPersonalized;
	}

	/**
	 * setPersonalized
	 * @param isPersonalized the isPersonalized to set
	 */
	private void setPersonalized(boolean isPersonalized) 
	{
		this.isPersonalized = isPersonalized;
	}

	/**
	 * isDatabaseLoaded
	 * @return the isDatabaseLoaded
	 */
	public boolean isDatabaseLoaded() 
	{
		return isDatabaseLoaded;
	}

	/**
	 * setDatabaseLoaded
	 * @param isDatabaseLoaded the isDatabaseLoaded to set
	 */
	private void setDatabaseLoaded(boolean isDatabaseLoaded) 
	{
		this.isDatabaseLoaded = isDatabaseLoaded;
	}
	
	/**
	 * isAuthenticated
	 * @return the isAuthenticated
	 */
	public boolean isAuthenticated() 
	{
		return isAuthenticated;
	}

	/**
	 * setAuthenticated
	 * @param isAuthenticated the isAuthenticated to set
	 */
	private void setAuthenticated(boolean isAuthenticated) 
	{
		this.isAuthenticated = isAuthenticated;
	}

//=====================================================================================//
//              TODO: IMPLEMENT USUAL CARD MANAGEMENT AND SECURITY FUNCTIONS           //
//=====================================================================================//
//
//	/**
//	 * verifyPin
//	 * @param cmd
//	 * @return
//	 * @throws ByteArrayToHexaStringException 
//	 */
//	private APDUResponse verifyPin(APDUCommand cmd) 
//	{
//		APDUResponse resp = new APDUResponse();
//		if (pinTryCounter>=pinMaxTry) 
//		{
//			resp.setStatusWord(APDUModel.SW_AUTH_METHOD_LOCKED);
//		}
//		
//		String pin = "";
//		
//		try 
//		{
//			pin = BytesTool.byteArrayToHexString(cmd.getData());
//		} 
//		catch (ByteArrayToHexaStringException e) 
//		{
//			e.printStackTrace();
//		}
//		
//		if (pin.equals(pinCode)) 
//		{
//			resp.setStatusWord(APDUModel.SW_SUCCESS);
//		} 
//		else 
//		{
//			pinTryCounter++;
//			int left = pinMaxTry-pinTryCounter;
//			
//			switch (left) 
//			{
//				case (0):
//					resp.setStatusWord(APDUModel.SW_VERIF_FAILED_0_LEFT);
//					break;
//				case (1):
//					resp.setStatusWord(APDUModel.SW_VERIF_FAILED_1_LEFT);
//					break;
//				case (2):
//					resp.setStatusWord(APDUModel.SW_VERIF_FAILED_2_LEFT);
//					break;
//				case (3):
//					resp.setStatusWord(APDUModel.SW_VERIF_FAILED_3_LEFT);
//					break;
//				default:
//					resp.setData(BytesTool.integerToByteArray(left, 1));
//					resp.setStatusWord(APDUModel.SW_VERIF_FAILED_COUNTER);
//					break;
//			}
//		}
//		return resp;
//	}
//
//	/**
//	 * changePin
//	 * @param cmd
//	 * @return
//	 */
//	private APDUResponse changePin(APDUCommand cmd) {
//		APDUResponse resp = new APDUResponse();
//		resp.setStatusWord(APDUModel.SW_SUCCESS);
//		return resp;
//	}
//
//	/**
//	 * getMessageCounter
//	 * @return the messageCounter
//	 */
//	public int getMessageCounter() 
//	{
//		return messageCounter;
//	}
//
//
//	/**
//	 * @return the isLocked
//	 */
//	public boolean isLocked() {
//		return isLocked;
//	}
//
//
//	/**
//	 * setLocked
//	 * @param isLocked the isLocked to set
//	 */
//	private boolean setLocked(boolean isLocked) 
//	{
//		if (isLocked && status!=Status.PERSONALIZED && status!=Status.SELECTABLE)
//			return false;
//		this.isLocked = isLocked;
//		if (isLocked()) {
//			setStatus(Status.LOCKED);
//		} else {
//			if (isPersonalized())
//				setStatus(Status.PERSONALIZED);
//			else
//				setStatus(Status.SELECTABLE);
//		}
//		return true;
//	}
//
//	/**
//	 * personalize
//	 * @param pinCode
//	 * @param key
//	 * @param expirationDate
//	 * @return String publicKey
//	 * @throws Exception 
//	 */
//	public synchronized PublicKey personalize(byte[] aID, String pinCode, int pinMaxTry, 
//						PublicKey clientKey, String algoType, Date expirationDate) throws Exception 
//	{
//		if (getStatus()!=Status.SELECTABLE && getStatus()!=Status.PERSONALIZED) 
//		{
//			// The applet must be initialized!
//			return null;
//		}
//		try 
//		{
//			// a keyPair is generated, the public key is returned
//			KeyPairGenerator kpg = KeyPairGenerator.getInstance(algoType); //algoType "RSA"...
//			kpg.initialize(1024);
//			KeyPair kp = kpg.genKeyPair();
//			this.publicKey = kp.getPublic();
//			this.privateKey = kp.getPrivate();
//			this.pinMaxTry = pinMaxTry;
//			this.clientKey = clientKey;
//			setStatus(Status.PERSONALIZED);
//			return publicKey;
//		} 
//		catch (Exception e) 
//		{
//			throw e;
//		}
//	}
//	
//	/**
//	 * putKey
//	 * @param cmd
//	 * @return
//	 */
//	private APDUResponse putKey(APDUCommand cmd) 
//	{
//		APDUResponse resp = new APDUResponse();
//		resp.setStatusWord(APDUModel.SW_SUCCESS);
//		return resp;
//	}
//
//	/**
//	 * getStatus
//	 * @param cmd
//	 * @return
//	 */
//	private APDUResponse getStatus(APDUCommand cmd) 
//	{
//		APDUResponse resp = new APDUResponse();
//		resp.setStatusWord(APDUModel.SW_SUCCESS);
//		return resp;
//	}
}